﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;

namespace DLC.Module.DataTable.ArchiveScriptableObject
{
    public class DataTable<T> : ScriptableObject, IDataTableRowType, IDataTable<T> where T : class, IDataRow, new()
    {
        [HideInInspector]
        [SerializeField]
        public string SheetName = "";

        [HideInInspector]
        [SerializeField]
        public string WorksheetName = "";

        // Note: initialize in OnEnable() not here.
        public T[] DataArray;

        [NonSerialized]
        private Dictionary<int, T> _datas = new Dictionary<int, T>();
        public T this[int id] => _datas[id];

        public string Name => Path.GetFileNameWithoutExtension(SheetName).Replace(' ','_');

        public string FullName => new TypeNamePair(Type, Name).ToString();

        public Type Type => typeof(T);

        public int Count => _datas.Count;
        [NonSerialized]
        private bool _isInitialized = false;
        //public bool AddDataRow(string dataRowString, object userData)
        //{
        //    throw new NotImplementedException();
        //}

        //public bool AddDataRow(byte[] dataRowBytes, int startIndex, int length, object userData)
        //{
        //    throw new NotImplementedException();
        //}

        public bool AddDataRow(T dataRow)
        {
            if (_datas.ContainsKey(dataRow.Id)) return false;
            _datas.Add(dataRow.Id, dataRow);
            return true;
        }

        public T[] GetAllDataRows()
        {
            return _datas.Values.ToArray();
        }

        public void GetAllDataRows(List<T> results)
        {
            results.AddRange(_datas.Values);
        }

        public T GetDataRow(int id)
        {
            T r = default(T);
            _datas.TryGetValue(id, out r);
            return r;
        }

        public T GetDataRow(Predicate<T> condition)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }

            foreach (T dataRow in _datas.Values)
            {
                if (condition(dataRow))
                {
                    return dataRow;
                }
            }

            return null;
        }

        public T[] GetDataRows(Predicate<T> condition)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }
            List<T> results = new List<T>();
            foreach (T dataRow in _datas.Values)
            {
                if (condition(dataRow))
                {
                    results.Add(dataRow);
                }
            }
            return results.ToArray();
        }

        public void GetDataRows(Predicate<T> condition, List<T> results)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }
            foreach (T dataRow in _datas.Values)
            {
                if (condition(dataRow))
                {
                    results.Add(dataRow);
                }
            }
        }

        public T[] GetDataRows(Comparison<T> comparison)
        {
            if (comparison == null)
            {
                throw new ArgumentNullException("Comparison is invalid.");
            }
            List<T> results = new List<T>();
            results.AddRange(_datas.Values);
            results.Sort(comparison);
            return results.ToArray();
        }

        public void GetDataRows(Comparison<T> comparison, List<T> results)
        {
            if (comparison == null)
            {
                throw new ArgumentNullException("Comparison is invalid.");
            }

            if (results == null)
            {
                throw new ArgumentNullException("Results is invalid.");
            }
            results.AddRange(_datas.Values);
            results.Sort(comparison);
        }

        public T[] GetDataRows(Predicate<T> condition, Comparison<T> comparison)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }
            if (comparison == null)
            {
                throw new ArgumentNullException("Comparison is invalid.");
            }
            List<T> results = new List<T>();
            foreach (var dataRow in _datas)
            {
                if (condition(dataRow.Value))
                {
                    results.Add(dataRow.Value);
                }
            }
            results.Sort(comparison);
            return results.ToArray();
        }

        public void GetDataRows(Predicate<T> condition, Comparison<T> comparison, List<T> results)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }
            if (comparison == null)
            {
                throw new ArgumentNullException("Comparison is invalid.");
            }
            foreach (var dataRow in _datas)
            {
                if (condition(dataRow.Value))
                {
                    results.Add(dataRow.Value);
                }
            }
            results.Sort(comparison);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return _datas.Values.GetEnumerator();
        }

        public bool HasDataRow(int id)
        {
            return _datas.ContainsKey(id);
        }

        public bool HasDataRow(Predicate<T> condition)
        {
            if (condition == null)
            {
                throw new ArgumentNullException("Condition is invalid.");
            }

            foreach (var dataRow in _datas)
            {
                if (condition(dataRow.Value))
                {
                    return true;
                }
            }

            return false;
        }

        public void RemoveAllDataRows()
        {
            _datas.Clear();
            _isInitialized = false;
        }

        public bool RemoveDataRow(int id)
        {
            if (!HasDataRow(id))
            {
                return false;
            }

            if (!_datas.Remove(id))
            {
                return false;
            }
            return true;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _datas.Values.GetEnumerator();
        }

        void OnEnable()
        {
            // Important:
            //    It should be checked an initialization of any collection data before it is initialized.
            //    Without this check, the array collection which already has its data get to be null 
            //    because OnEnable is called whenever Unity builds.
            // 		
            if (DataArray == null)
                DataArray = new T[0];
            // 初始化_datas
            InitializeDatas();
        }
        public virtual void InitializeDatas()
        {
            if (DataArray == null)
            {
                return;
            }
            // 初始化_datas
            if (!_isInitialized)
            {
                _isInitialized = true;
                foreach (var data in DataArray)
                {
                    AddDataRow(data);
                }
            }
        }

        public Type GetDataTableRowType()
        {
            return Type;
        }
    }
}
